# -*- coding: utf-8 -*-
# created on 2016/11/1

from itertools import chain
from sympy import Union, Interval, Intersection, FiniteSet, EmptySet, latex, sympify
from mathsolver.functions.hanshu.dingyiyu import QiuDingYiYu
from mathsolver.functions.hanshu.base import get_cond, is_piecefunc, is_constantfunc
from mathsolver.functions.base import BaseNumber, BaseFunction, BasePieceFunc, BaseValue, BaseFunc, BaseInter
from mathsolver.functions.hanshu.helper import check_func, check_inter, limit
from mathsolver.functions.mathematica.mathematicaSolve import MathematicaSimplify
from mathsolver.functions.hanshu.zuizhi_new import FunctionRange, MinValue, MaxValue, MinMaxValue


def zhiyu_singleinterval(func_expr, var, interval, dizen=True, belong=None):
    """根据单调性求函数在单个单调区上的值域"""
    if not interval:
        return EmptySet()
    elif isinstance(interval, Interval):
        left, right, lopen, ropen = interval.args
        left_val = limit(func_expr, var, left, dir="+", assumptions=belong)
        right_val = limit(func_expr, var, right, dir="-", assumptions=belong)

        if dizen:
            inter_args = [left_val, right_val, lopen, ropen]
        else:
            inter_args = [right_val, left_val, ropen, lopen]

        # 特殊处理 如果 left_val 或者 right_val 里面含有 (-1)**(m/n) Interval 初始化报错，用 Mathematica 化简
        try:
            return Interval(*inter_args)
        except Exception:
            inter_arg0 = MathematicaSimplify().solver(BaseNumber(inter_args[0])).output[0].value
            inter_arg1 = MathematicaSimplify().solver(BaseNumber(inter_args[1])).output[0].value
            return Interval(sympify(str(inter_arg0)), sympify(str(inter_arg1)), inter_args[2], inter_args[3])
    elif isinstance(interval, Union):
        return Union(*(zhiyu_singleinterval(func_expr, var, interval, dizen, belong) for interval in interval.args))
    elif isinstance(interval, FiniteSet):
        return FiniteSet(func_expr)


class ZhiYuDanDiaoXing(BaseFunction):
    """根据单调性求值域（不含参数）"""

    def solver(self, *args):
        func, cond = args
        expr, var = func.expression, func.var
        # 求函数单调性
        from mathsolver.functions.hanshu.dandiaoqujian import DanDiaoQuJian
        dandiao_solver = DanDiaoQuJian().solver(func, cond)
        self.steps.extend(dandiao_solver.steps)
        up, down = dandiao_solver.output[0].value
        if isinstance(up, list):
            # 含参数的情况
            zhiyu = []
            for i in range(len(up)):
                belong = up[i][0]
                up_piece, down_piece = up[i][1], down[i][1]
                zhiyu_piece = Union(zhiyu_singleinterval(expr, var, up_piece, True, belong), zhiyu_singleinterval(expr, var, down_piece, False, belong))
                zhiyu.append([belong, zhiyu_piece])
            fmt = ""
            res = []
            for belong, zhiyu_piece in zhiyu:
                if zhiyu_piece:
                    fmt += "当 %s 时，函数的值域为 %s "
                    res.extend([belong.printing(), latex(zhiyu_piece)])
            self.steps.append(["", fmt % tuple(res)])
        else:
            zhiyu = Union(zhiyu_singleinterval(expr, var, up, True), zhiyu_singleinterval(expr, var, down, False))
            self.steps.append(["", "函数 %s 的值域为 %s" % (func.printing(), latex(zhiyu))])
        self.output.append(BaseValue(zhiyu))
        self.label.add("运用单调区间求函数值域/最值")
        return self


class ChangShuZhiYu(BaseFunction):
    """常数值域：y = a"""

    def solver(self, *args):
        func, cond = args
        f = func.expression
        self.steps.append(["", "常函数 %s 的值域为 %s" % (func.printing(), f)])
        self.label.add("常值函数的值域/最值")
        self.output.append(BaseNumber(f))
        return self


class FenDuanHanShuZhiYu(ZhiYuDanDiaoXing):
    """分段函数值域"""

    def solver(self, *args):
        func, cond = args
        f_name, f, symbol = func.name, func.expression, func.var

        self.steps.append(["", "分别求出分段函数在各段的值域"])
        piece_zhiyus = []
        for f_exp, f_interval in f:
            dingyiyu = cond.intersection(f_interval)
            piece_zhiyu = HanShuZhiYu().solver(BaseFunc({"var": symbol, "name": f_name, "type": "", "expression": f_exp}), dingyiyu).output[0].value
            piece_zhiyus.append(piece_zhiyu)
            self.steps.append(["", "函数在区间 {} 上的值域为 {}".format(latex(f_interval), latex(piece_zhiyu))])

        zhiyu = Union(piece_zhiyus)

        self.steps.append(["", "分段函数的值域为 {}".format(latex(zhiyu))])
        self.output.append(BaseValue(zhiyu))
        self.label.add("分段函数的的值域/最值问题")
        return self


class HanShuZhiYu(BaseFunction):
    """单调性求函数值域"""

    def solver(self, *args):
        # 处理输入
        func = check_func(args[0])
        f_name, expr, var = func.name, func.expression, func.var

        # 2. 考察表达式含不含参数
        if is_piecefunc(func):
            canshu_symbol = list(set(chain(*[expr_1.free_symbols for expr_1, _ in expr])) - {var})
            is_sanjiao = False
        else:
            from mathsolver.functions.sanjiao.sanjiao_utils import is_trig_f
            canshu_symbol = list(expr.free_symbols - {var})
            is_sanjiao = is_trig_f(expr)

        # 2.1 如果含有参数：调用 Function_range 函数

        if canshu_symbol or is_sanjiao:
            return FunctionRange(self.known).solver(*args)

        # 2.2 否则运用单调性求值域
        if isinstance(func, BasePieceFunc):
            cond = get_cond(args)
        else:
            # 求函数定义域
            if func.dingyiyu:
                dingyiyu = check_inter(func.dingyiyu)
            else:
                dingyiyu = QiuDingYiYu().solver(func)

            cond = get_cond(args).intersection(dingyiyu)
            # 补充：如果题目给定范围
            known_var_range = self.search(var)
            if known_var_range:
                cond = Intersection(cond, known_var_range)

        # 补充：并区间处理
        if isinstance(cond, Union):
            return self.zhiyu_unioninterval(func, cond)

        if is_piecefunc(func):
            return FenDuanHanShuZhiYu().solver(func, cond)
        elif is_constantfunc(func):
            return ChangShuZhiYu().solver(func, cond)
        else:
            return ZhiYuDanDiaoXing().solver(func, cond)

    def zhiyu_unioninterval(self, func, cond):
        """如果 cond 为并区间，分别计算函数在单个区间上面的值域，然后取并集"""

        self.steps.append(["", "定义区间为复合区间，分别考虑函数在单个区间上的值域"])
        zhiyus = []
        for interval in cond.args:
            stepsolver = HanShuZhiYu().solver(func, interval)
            self.steps.extend(stepsolver.steps)
            zhiyus.append(stepsolver.output[0].value)

        # 考虑含参数情况
        if isinstance(zhiyus[0], list):
            zhiyu = []
            for zhiyu_piece in zip(*zhiyus):
                zhi = Union(ll[1] for ll in zhiyu_piece)
                dingyi = zhiyu_piece[0][0]
                zhiyu.append([dingyi, zhi])

            fmt = "综合考虑可知，"
            res = []
            for belong, zhiyu_piece in zhiyu:
                if zhiyu_piece:
                    fmt += "当 %s 时，函数的值域为 %s "
                    res.extend([belong.printing(), latex(zhiyu_piece)])
            self.steps.append(["", fmt % tuple(res)])
        else:
            zhiyu = Union(zhiyus)
            self.steps.append(
                ["", "综合考虑可知函数 {} 在区间 {} 上的值域为 {}".format(func.printing(), latex(cond), latex(zhiyu))])
        self.label.add("求函数值域")
        self.output.append(BaseInter(zhiyu))
        return self


hanshuzuizhi = MinMaxValue  # 求函数最值
hanshuzuidazhi = MaxValue  # 函数最大值
hanshuzuixiaozhi = MinValue  # 函数最小值

if __name__ == '__main__':
    pass
